
#pragma once
#include "log_def.h" //日志定义，不同项目需要修改
#include "Rpc.h"
#include "../external/libevent_cpp/include/include_all.h"

template<class Con>
class RpcConToClient : public lc::SvrCon
{
public:
	virtual ~RpcConToClient() override;
	virtual void OnRecv(const lc::MsgPack& msg) override final;
	virtual void OnConnected() override final;
	virtual void OnCon() = 0; //代替 OnConnected 用

public:
	template<class RspCb, class ... Args>
	void CallTimeOut(RspCb cb, RpcClientImp::RpcTimeOut timeOut, const std::string& funName, Args&& ... args);
	template<class RspCb, class ... Args>
	void CallTimeOut(RspCb cb, RpcClientImp::RpcTimeOut timeOut, char cmd, Args&& ... args);

	template<class RspCb, class ... Args>
	void Call(RspCb cb, const std::string& funName, Args&& ... args);

	template<class RspCb, class ... Args>
	void Call(RspCb cb, char cmd, Args&& ... args);

	template<class ... Args>
	void Call(char cmd, Args&& ... args);

	template<class ... Args>
	RpcClientImp::CallRetHelper CallRet(const char* funName, Args&& ... args);


	template<class ... Args>
	void Call(const char* funName, Args&& ... args)
	{
		CallTimeOut(nullptr, nullptr, std::string(funName), std::forward<Args>(args) ...);
	}
	//Call("fun", const string&) 会重载歧义，提供这个准确区分的函数
	template<class ... Args>
	void CallNoRet(const std::string& funName, Args&& ... args)
	{
		CallTimeOut(nullptr, nullptr, funName, std::forward<Args>(args) ...);
	}

};


struct ConSnId
{
	uint64_t conId = 0;
	uint32_t snId = 0;
};
//Con 必须继承 RpcConToClient<Con>
template<class Con>
class RpcNetSvr : public RpcSvr<Con>
{
public:
	lc::Listener<Con> m_netSvr;

private:
//	lc::Timer m_timer; 

public:
	static RpcNetSvr& Ins()//进程结束，RpcNetSvr不用释放，因为目前代码结构，释放的时候 Con又会调用已释放的RpcNetSvr
	{
		static RpcNetSvr *obj = new RpcNetSvr();
		return *obj;
	}

	bool Init(unsigned short listen_port, const char* listen_ip = nullptr)
	{
		auto send = [](Con& con, const char* data, size_t len)
		{
			con.SendPack(data, len);
		};
		auto OnConnectInit = [](Con& con)
		{
			con.OnCon();
		};
		RpcSvr<Con>::Init(send, OnConnectInit);

		//不需要了，让发送请求的时候，自动检查。
		//auto f = []()
		//{
		//	RpcNetSvr::Ins().CheckTimeOut(10);
		//};
		//m_timer.Start(10, f, true);

		return m_netSvr.Init(listen_port, listen_ip);
	}

	ConSnId GetCurConSnId()
	{
		ConSnId id;
		Con *pCon = RpcSvr<Con>::GetCurCon();
		L_COND(pCon, id);
		id.conId = pCon->GetId();
		id.snId = RpcSvr<Con>::GetCurSnId();
		return id;
	}

	template<class ... Args>
	void RetEx(const ConSnId& id, Args&& ... args)
	{
		Con* pCon = dynamic_cast<Con*>(RpcNetSvr<Con>::Ins().m_netSvr.GetConnMgr().FindConn(id.conId));
		L_COND_V(pCon);
		RpcSvr<Con>::RetEx(*pCon, id.snId, std::forward<Args>(args)...);
	}
};

//rpc client
//默认自动重连
class RpcNetClient : public RpcClient, private lc::ClientCon //private ClientCon 减少用户错误调用基类函数的机会
{
	using PRpcNetClient = RpcNetClient*;
	static const uint32_t RE_CON_INTERVAL_SEC = 10; //x秒尝试重连

	lc::Timer m_timer;
	lc::Timer m_rconTimer;
	std::function<void()> m_OnInitOk = nullptr;
public:
	//要求单线程调用才正确。可以单线程内多个 RpcNetClient对象
	static RpcNetClient* GetCurCon()
	{
		return CurCon();
	}	//只能服务器函数内部调用

	template<class ... Args>
	static void Ret(Args&& ... args)
	{
		RpcNetClient* p = GetCurCon();
		L_COND_V(p);
		p->Ret(std::forward<Args>(args)...);
	}

	//@onInitOk rpc连接或者重连初始化完成后调用。 能保证当前RpcNetClient生存期有效
	bool Init(const char* ip, unsigned short port, std::function<void()> onInitOk = nullptr)
	{
		auto send = [this](const char* data, size_t len)//回调的时机就是本对象基类的成员函数，保证了this不野
		{
			SendPack(data, len);
		};

		RpcClient::Init(send, onInitOk);
		auto f = [this]() //m_timer 是this成员，保证了this不野
		{
			CheckTimeOut(10);
		};
		m_timer.Start(10, f, true);

		return ConnectInit(ip, port);
	}

	//断开连接，不再重连
	void DisConnect()
	{
		m_rconTimer.Stop();
		lc::ClientCon::DisConnect();
	}

	const char* GetRemoteIp() const
	{
		return lc::ClientCon::GetRemoteIp();
	}
	uint16_t GetRemotePort() const
	{
		return lc::ClientCon::GetRemotePort();
	}


	virtual void OnDisconnected() override
	{
		L_DEBUG("start try recon timer , sec=%d remote addr = %s %d", RE_CON_INTERVAL_SEC, GetRemoteIp(), GetRemotePort());
		CheckTimeOut(0);
		auto f = [this]()
		{
			TryReconnect();
		};
		m_rconTimer.Stop();
		m_rconTimer.Start(RE_CON_INTERVAL_SEC, f);
	}

private:
	static PRpcNetClient &CurCon()
	{
		static RpcNetClient* curClient = nullptr;
		return curClient;
	}


	virtual void OnRecv(const lc::MsgPack& msg) override final
	{
		CurCon() = this;
		Rev(msg.data, msg.len);
		CurCon() = nullptr;
	}

	virtual void OnConnected() override final
	{
		ReqInit();
	}

};


template<class Con>
RpcConToClient<Con>::~RpcConToClient()
{
	RpcNetSvr<Con>::Ins().Discon(GetId());
}

template<class Con>
void RpcConToClient<Con>::OnRecv(const lc::MsgPack& msg)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().Rev(*p, msg.data, msg.len);
}

template<class Con>
void RpcConToClient<Con>::OnConnected()
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().Connect(*p);
}

template<class Con>
template<class RspCb, class ... Args >
void RpcConToClient<Con>::CallTimeOut(RspCb cb, RpcClientImp::RpcTimeOut timeOut, const std::string& funName, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().CallTimeOut(*p, cb, timeOut, funName, std::forward<Args>(args) ...);
}

template<class Con>
template<class RspCb, class ... Args>
void RpcConToClient<Con>::CallTimeOut(RspCb cb, RpcClientImp::RpcTimeOut timeOut, char cmd, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().CallTimeOut(*p, cb, timeOut, cmd, std::forward<Args>(args) ...);
}

template<class Con>
template<class RspCb, class ... Args >
void RpcConToClient<Con>::Call(RspCb cb, const std::string& funName, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().Call(*p, cb, funName, std::forward<Args>(args) ...);
}

template<class Con>
template<class RspCb, class ... Args >
void RpcConToClient<Con>::Call(RspCb cb, char cmd, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().Call(*p, cb, cmd, std::forward<Args>(args) ...);
}

template<class Con>
template<class ... Args >
void RpcConToClient<Con>::Call(char cmd, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	RpcNetSvr<Con>::Ins().Call(*p, nullptr, cmd, std::forward<Args>(args) ...);
}

template<class Con>
template<class ... Args >
RpcClientImp::CallRetHelper  RpcConToClient<Con>::CallRet(const char* funName, Args&& ... args)
{
	Con* p = dynamic_cast<Con*>(this);
	return RpcNetSvr<Con>::Ins().CallRet(*p, funName, std::forward<Args>(args) ...);
}